#![no_std]
#![doc = "Timer driver for Rockchip RK3588 SoC - 2-Channel TIMER"]

use core::ptr::{read_volatile, write_volatile};

pub mod regs;
pub use regs::{TimerMode, TimerType};

/// RK3588 2-Channel Timer Driver
///
/// This driver supports the 2-channel Normal Count TIMER on RK3588.
/// According to the TRM, 2-channel timers typically have both channels
/// configured as increment counters (NICTIMER).
///
/// # Features
/// - 64-bit counter support (32-bit x 2 registers)
/// - Two counting modes: free-running and user-defined count
/// - Maskable interrupts
/// - 24MHz clock source
///
/// # Example
/// ```
/// use timer_rk3588::{RK3588Timer, TimerMode};
///
/// // Create timer for channel 0 of TIMER_PMU
/// let timer = RK3588Timer::new(0xFD8F0000, 0);
///
/// // Initialize: disable, set free-running mode, unmask interrupt
/// timer.disable();
/// timer.set_mode(TimerMode::FreeRunning);
/// timer.enable_interrupt();
///
/// // Load count value (e.g., 1 second at 24MHz)
/// timer.load_count(24_000_000);
///
/// // Enable timer
/// timer.enable();
/// ```
pub struct RK3588Timer {
    base_addr: usize,
    channel: u8,
    timer_type: TimerType,
}

impl RK3588Timer {
    /// Create a new timer instance
    ///
    /// # Parameters
    /// - `base_addr`: Timer module base address (e.g., TIMER_PMU = 0xFD8F0000)
    /// - `channel`: Channel number (0 or 1 for 2-channel timer)
    ///
    /// # Example
    /// ```
    /// // TIMER_PMU channel 0
    /// let timer0 = RK3588Timer::new(0xFD8F0000, 0);
    ///
    /// // TIMER_PMU channel 1
    /// let timer1 = RK3588Timer::new(0xFD8F0000, 1);
    /// ```
    pub const fn new(base_addr: usize, channel: u8) -> Self {
        Self {
            base_addr,
            channel,
            // Default to increment type, will be detected in init
            timer_type: TimerType::Increment,
        }
    }

    /// Get the actual register base address for this channel
    #[inline]
    fn channel_base(&self) -> usize {
        self.base_addr + (self.channel as usize) * regs::offset::CHANNEL_OFFSET
    }

    /// Read register
    #[inline]
    fn read_reg(&self, offset: usize) -> u32 {
        unsafe { read_volatile((self.channel_base() + offset) as *const u32) }
    }

    /// Write register
    #[inline]
    fn write_reg(&self, offset: usize, value: u32) {
        unsafe { write_volatile((self.channel_base() + offset) as *mut u32, value) }
    }

    /// Read timer module revision register (at module base, not channel base)
    #[inline]
    fn read_revision(&self) -> u32 {
        unsafe { read_volatile((self.base_addr + regs::offset::REVISION) as *const u32) }
    }

    /// Initialize and detect timer type from revision register
    ///
    /// Returns true if the timer type was successfully detected
    pub fn init(&mut self) -> bool {
        let revision = self.read_revision();

        // Check if this is a 2-channel timer
        if (revision & 0xFFFF0000) == (regs::revision_2ch::EXPECTED_VALUE & 0xFFFF0000) {
            // Detect channel type
            let ch_type_bit = if self.channel == 0 {
                regs::revision_2ch::CH0_TYPE_SHIFT
            } else {
                regs::revision_2ch::CH1_TYPE_SHIFT
            };

            self.timer_type = if (revision & (1 << ch_type_bit)) != 0 {
                TimerType::Increment
            } else {
                TimerType::Decrement
            };

            true
        } else {
            false
        }
    }

    /// Get timer type (increment or decrement)
    pub fn timer_type(&self) -> TimerType {
        self.timer_type
    }

    /// Disable timer
    ///
    /// Must be called before reconfiguring timer registers
    pub fn disable(&self) {
        self.write_reg(regs::offset::CONTROL_REG, 0);
    }

    /// Enable timer
    pub fn enable(&self) {
        let ctrl = self.read_reg(regs::offset::CONTROL_REG);
        self.write_reg(
            regs::offset::CONTROL_REG,
            ctrl | regs::control::TIMER_ENABLE,
        );
    }

    /// Check if timer is enabled
    pub fn is_enabled(&self) -> bool {
        (self.read_reg(regs::offset::CONTROL_REG) & regs::control::TIMER_ENABLE) != 0
    }

    /// Set timer mode
    ///
    /// # Parameters
    /// - `mode`: TimerMode::FreeRunning or TimerMode::UserDefined
    ///
    /// # Note
    /// Timer must be disabled before calling this
    pub fn set_mode(&self, mode: TimerMode) {
        let ctrl = self.read_reg(regs::offset::CONTROL_REG);
        let new_ctrl = (ctrl & !regs::control::TIMER_MODE_MASK) | mode.to_bits();
        self.write_reg(regs::offset::CONTROL_REG, new_ctrl);
    }

    /// Enable interrupt
    pub fn enable_interrupt(&self) {
        let ctrl = self.read_reg(regs::offset::CONTROL_REG);
        self.write_reg(
            regs::offset::CONTROL_REG,
            ctrl | regs::control::TIMER_INT_ENABLE,
        );
    }

    /// Disable interrupt (mask)
    pub fn disable_interrupt(&self) {
        let ctrl = self.read_reg(regs::offset::CONTROL_REG);
        self.write_reg(
            regs::offset::CONTROL_REG,
            ctrl & !regs::control::TIMER_INT_ENABLE,
        );
    }

    /// Load count value (64-bit)
    ///
    /// # Parameters
    /// - `count`: Count value to load
    ///
    /// # Behavior
    /// For decrement timer (NDCTIMER): This is the value to count down from
    /// For increment timer (NICTIMER): This is the target value to count up to
    ///
    /// # Note
    /// Timer should be disabled when loading count value
    pub fn load_count(&self, count: u64) {
        let count0 = (count & 0xFFFFFFFF) as u32;
        let count1 = ((count >> 32) & 0xFFFFFFFF) as u32;

        self.write_reg(regs::offset::LOAD_COUNT0, count0);
        self.write_reg(regs::offset::LOAD_COUNT1, count1);
    }

    /// Read current counter value (64-bit)
    ///
    /// # Returns
    /// Current timer count value
    ///
    /// # Note
    /// For decrement timer: counts down from load_count to 0
    /// For increment timer: counts up from 0 to load_count
    pub fn current_value(&self) -> u64 {
        let value0 = self.read_reg(regs::offset::CURRENT_VALUE0);
        let value1 = self.read_reg(regs::offset::CURRENT_VALUE1);

        ((value1 as u64) << 32) | (value0 as u64)
    }

    /// Get interrupt status
    ///
    /// # Returns
    /// true if interrupt is pending
    pub fn get_interrupt_status(&self) -> bool {
        (self.read_reg(regs::offset::INT_STATUS) & 0x1) != 0
    }

    /// Clear interrupt status
    ///
    /// Write 1 to clear the interrupt
    pub fn clear_interrupt(&self) {
        self.write_reg(regs::offset::INT_STATUS, 0x1);
    }

    /// Configure and start timer in one step
    ///
    /// # Parameters
    /// - `count`: Count value
    /// - `mode`: Timer mode (free-running or user-defined)
    /// - `enable_irq`: Whether to enable interrupt
    ///
    /// # Example
    /// ```
    /// // Start a 1-second periodic timer at 24MHz with interrupt
    /// timer.start(24_000_000, TimerMode::FreeRunning, true);
    /// ```
    pub fn start(&self, count: u64, mode: TimerMode, enable_irq: bool) {
        // Step 1: Disable timer
        self.disable();

        // Step 2: Configure mode and interrupt
        let mut ctrl = mode.to_bits();
        if enable_irq {
            ctrl |= regs::control::TIMER_INT_ENABLE;
        }
        self.write_reg(regs::offset::CONTROL_REG, ctrl);

        // Step 3: Load count value
        self.load_count(count);

        // Step 4: Enable timer
        self.enable();
    }

    /// Stop timer
    pub fn stop(&self) {
        self.disable();
    }

    /// Get timer revision information
    pub fn get_revision(&self) -> u32 {
        self.read_revision()
    }
}

// Implement Send and Sync for multi-core usage
unsafe impl Send for RK3588Timer {}
unsafe impl Sync for RK3588Timer {}

/// Timer module for managing multiple channels
pub struct RK3588TimerModule {
    base_addr: usize,
}

impl RK3588TimerModule {
    /// Create new timer module
    pub const fn new(base_addr: usize) -> Self {
        Self { base_addr }
    }

    /// Get timer for specific channel
    pub fn channel(&self, channel: u8) -> RK3588Timer {
        RK3588Timer::new(self.base_addr, channel)
    }

    /// Get revision
    pub fn get_revision(&self) -> u32 {
        unsafe { read_volatile((self.base_addr + regs::offset::REVISION) as *const u32) }
    }
}

/// Pre-defined timer modules for RK3588
pub mod timers {
    use super::RK3588TimerModule;
    use crate::regs::base_addr;

    /// TIMER_PMU: 2-channel timer in pd_pmu1 power domain
    pub const TIMER_PMU: RK3588TimerModule = RK3588TimerModule::new(base_addr::TIMER_PMU);

    /// TIMER_NPU: 2-channel timer in pd_npu power domain
    pub const TIMER_NPU: RK3588TimerModule = RK3588TimerModule::new(base_addr::TIMER_NPU);

    /// TIMER_DDR: 2-channel timer in pd_center power domain
    pub const TIMER_DDR: RK3588TimerModule = RK3588TimerModule::new(base_addr::TIMER_DDR);
}
